package com.xiaoxiang.cameracontrol.zsocket;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * @author 鹄望潇湘
 * @className: SocketServer
 * @date 2021-12-08 13:50
 */
public abstract class SocketServer {
    protected Logger logger=null;
    private String host = null;
    private int port = 0;
    protected ServerSocket socket = null;
    private boolean stop  = false;
    private HashMap<String, Socket> connections;

    private void init_logger() {
        this.logger = Logger.getLogger(SocketClient.class.getName());
        this.logger.setLevel(Level.INFO);
    }

    public SocketServer(){
        this.init_logger();
        this.connections = new HashMap<String, Socket>();
    }

    public void start(String ip, int port) {
        this.host = ip;
        this.port = port;
        this.stop = false;
        try {
            InetAddress addr = InetAddress.getByName(ip);
            this.socket = new ServerSocket(port, 50, addr);
        } catch (IOException e) {
            this.stop = true;
            this.logger.info("服务器启动失败: "+e.getMessage());
            return;
        }
        this.logger.info("服务器启动成功:"+ip+":"+String.valueOf(port));
        while(!this.stop){
            try {
                Socket socket = this.socket.accept();
                String address = socket.getInetAddress().getHostAddress()+":"+String.valueOf(socket.getPort());
                this.connections.put(address, socket);
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        message_handle(socket, address);
                    }
                }).start();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }

    }

    abstract public void connected(String address);

    private void message_handle(Socket socket, String address){
        this.connected(address);
        InputStream in = null;
        try {
            in = socket.getInputStream();
        } catch (IOException e) {
            this.logger.info("无法获取socket的输入流，连接即将关闭");
            this.disconnected(address);
            return;
        }
        byte[] buffer = new byte[0];
        while(true){
            if(this.stop) break;

            byte[] data = new byte[4096];
            int count = 0;
            try {
                count = in.read(data);
                if(count==0) {
                    this.logger.info("未读取到任何数据");
                    continue;
                }
                data = FrameMessage.sub_list(data, 0, count);
                buffer = FrameMessage.att_list(buffer, data);
                while(true) {
                    FrameMessage m = new FrameMessage(buffer);
                    buffer = m.get_remain_message();
                    if(m.get_complete_message().length>0) {
                        this.message_process(m.get_complete_message(), address);
                    }else break;
                }
            } catch (IOException e) {
                this.logger.info("IO异常， 即将关闭连接");
                this.disconnected(address);
                return;
            }
        }
    }

    public void disconnect(String address){
        Socket socket = this.connections.get(address);
        if(socket != null){
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            this.connections.remove(address);
        }
        this.disconnected(address);
    }

    abstract  public void message_process(byte[] message, String address);

    abstract  public void disconnected(String address);

    public void closeServer(){
        this.stop = true;
        this.host = null;
        this.port = 0;
        for(String key : this.connections.keySet()){
            Socket socket = this.connections.get(key);
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        this.connections.clear();
        try {
            this.socket.close();
            this.socket = null;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void send_bytes(byte[] data, String address){
        Socket socket = this.connections.get(address);
        if(socket==null) return;
        try {
            OutputStream out = socket.getOutputStream();
            byte[] head = FrameMessage.int_to_bytes(data.length, "big");
            out.write(FrameMessage.att_list(head, data));
            out.flush();
        } catch (IOException e) {
            this.logger.info("IO错误，未发送消息");
        }

    }

    public void send_string(String s, String address){
        this.send_bytes(s.getBytes(StandardCharsets.UTF_8), address);
    }
}
